package com.smsc.headend.connector.aop;


import cn.hutool.core.util.IdUtil;
import com.smsc.headend.common.thread.UDISThreadFactory;
import com.smsc.headend.connector.annotations.DeviceSignInLimiter;
import com.smsc.headend.connector.enums.states.ChannelState;
import com.smsc.headend.connector.netty.NettyConfigHelper;
import com.smsc.headend.connector.service.DeviceConnectService;
import com.smsc.headend.connector.service.protocol.DLMSPacketCodecService;
import com.smsc.headend.connector.utils.NetUtil;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.sql.Time;
import java.util.concurrent.*;

@Aspect
@Component
@Slf4j
public class LimiterAspect {
    @Autowired
    @Qualifier("tokenPool")
    LinkedBlockingQueue<String> tokenPool;

    @Pointcut("@annotation(com.smsc.headend.connector.annotations.DeviceSignInLimiter)")
    public void pointCut() {
    }

    @Value("${client.establish.limiter.interval:200}")
    Integer interval;

    ScheduledExecutorService ticketService = Executors.newScheduledThreadPool(1, new UDISThreadFactory("sign-tik"));

    @PostConstruct
    public void scheduleHandlePool() {
        ticketService.scheduleAtFixedRate(() -> {
            try {
                if (tokenPool.remainingCapacity() > 0) {
                    log.debug("signInLimiterTicket++");
                    tokenPool.add(IdUtil.fastUUID());
                }
            }
            catch (Exception e) {
                log.error("add signInLimitTicket thread exception", e);
            }
        }, 15000, interval, TimeUnit.MILLISECONDS);
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Channel channel = (Channel) joinPoint.getArgs()[0];
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            DeviceSignInLimiter deviceLimiter = AnnotationUtils.getAnnotation(method, DeviceSignInLimiter.class);
            if (deviceLimiter == null) {
                return null;
            }
            String token = tokenPool.poll();
            if (token == null) {
                log.info("limitDeviceSignIn,wait for reconnect", NetUtil.getIpFromChannel(channel));
                channel.attr(NettyConfigHelper.channelState).set(ChannelState.WaitingReg);
                return null;
            } else {
                Object proceed = joinPoint.proceed();
                channel.attr(NettyConfigHelper.channelState).set(ChannelState.Registered);
                return proceed;
            }
        } catch (Throwable e) {
            channel.attr(NettyConfigHelper.channelState).set(ChannelState.WaitingReg);
            log.error("errorIn signIn:{}, {}", joinPoint.getSignature(), e.getMessage());
            log.debug("", e);
        }
        return null;
    }


}
